//! 沙箱放置引擎
//! 
//! 负责决定沙箱在哪个节点上运行

use std::collections::HashMap;
use std::cmp::Ordering;
use serde::{Serialize, Deserialize};
use anyhow::{Result, anyhow};

use crate::scheduler::scheduler::NodeInfo;
use crate::types::ResourceRequirements;

/// 放置策略
#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
pub enum PlacementStrategy {
    /// 最佳适配：选择资源最匹配的节点
    BestFit,
    /// 最差适配：选择资源最富裕的节点
    WorstFit,
    /// 首次适配：选择第一个满足条件的节点
    FirstFit,
    /// 轮询：轮流选择节点
    RoundRobin,
    /// 负载均衡：选择负载最低的节点
    LeastLoaded,
    /// 资源感知：综合考虑资源和负载
    ResourceAware,
}

/// 放置引擎
pub struct PlacementEngine {
    /// 放置策略
    strategy: PlacementStrategy,
    /// 轮询计数器
    round_robin_counter: std::sync::atomic::AtomicUsize,
}

/// 节点选择器
pub struct NodeSelector {
    /// 节点权重
    weights: HashMap<String, f64>,
    /// 亲和性规则
    affinity_rules: Vec<NodeAffinityRule>,
    /// 反亲和性规则
    anti_affinity_rules: Vec<NodeAffinityRule>,
}

/// 节点亲和性规则
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct NodeAffinityRule {
    /// 规则名称
    pub name: String,
    /// 匹配标签
    pub label_selector: HashMap<String, String>,
    /// 权重（软约束）
    pub weight: Option<f64>,
    /// 是否为硬约束
    pub required: bool,
}

/// 放置评分
#[derive(Debug, Clone)]
struct PlacementScore {
    /// 节点ID
    node_id: String,
    /// 评分
    score: f64,
    /// 评分详情
    details: PlacementScoreDetails,
}

/// 评分详情
#[derive(Debug, Clone)]
struct PlacementScoreDetails {
    /// 资源适配度
    resource_fit: f64,
    /// 负载评分
    load_score: f64,
    /// 亲和性评分
    affinity_score: f64,
    /// 地理位置评分
    location_score: f64,
}

impl PlacementEngine {
    /// 创建新的放置引擎
    pub fn new(strategy: PlacementStrategy) -> Self {
        Self {
            strategy,
            round_robin_counter: std::sync::atomic::AtomicUsize::new(0),
        }
    }

    /// 选择最佳节点
    pub async fn select_node(
        &self,
        nodes: &[NodeInfo],
        requirements: &ResourceRequirements,
        selector: Option<&NodeSelector>,
    ) -> Result<Option<String>> {
        if nodes.is_empty() {
            return Ok(None);
        }

        // 过滤掉不满足资源需求的节点
        let candidates: Vec<_> = nodes
            .iter()
            .filter(|node| self.node_meets_requirements(node, requirements))
            .collect();

        if candidates.is_empty() {
            return Ok(None);
        }

        // 根据策略选择节点
        let selected_node = match self.strategy {
            PlacementStrategy::BestFit => self.select_best_fit(&candidates, requirements),
            PlacementStrategy::WorstFit => self.select_worst_fit(&candidates, requirements),
            PlacementStrategy::FirstFit => self.select_first_fit(&candidates),
            PlacementStrategy::RoundRobin => self.select_round_robin(&candidates),
            PlacementStrategy::LeastLoaded => self.select_least_loaded(&candidates),
            PlacementStrategy::ResourceAware => self.select_resource_aware(&candidates, requirements, selector),
        }?;

        Ok(Some(selected_node.node_id.clone()))
    }

    /// 检查节点是否满足资源需求
    fn node_meets_requirements(&self, node: &NodeInfo, requirements: &ResourceRequirements) -> bool {
        node.available_resources.memory_mb >= requirements.memory_mb
            && node.available_resources.cpu_cores >= requirements.cpu_cores
            && node.available_resources.disk_mb >= requirements.disk_mb
            && node.available_resources.network_bandwidth_mbps >= requirements.network_bandwidth_mbps
            && node.load_percentage < 95.0 // 不超过95%负载
    }

    /// 最佳适配选择
    fn select_best_fit<'a>(&self, candidates: &[&'a NodeInfo], requirements: &ResourceRequirements) -> Result<&'a NodeInfo> {
        let mut scores: Vec<_> = candidates
            .iter()
            .map(|node| {
                let fit_score = self.calculate_fit_score(node, requirements);
                PlacementScore {
                    node_id: node.node_id.clone(),
                    score: fit_score,
                    details: PlacementScoreDetails {
                        resource_fit: fit_score,
                        load_score: 1.0 - node.load_percentage / 100.0,
                        affinity_score: 0.0,
                        location_score: 0.0,
                    },
                }
            })
            .collect();

        scores.sort_by(|a, b| b.score.partial_cmp(&a.score).unwrap_or(Ordering::Equal));
        
        let selected_node_id = &scores[0].node_id;
        candidates
            .iter()
            .find(|node| node.node_id == *selected_node_id)
            .copied()
            .ok_or_else(|| anyhow!("未找到选中的节点"))
    }

    /// 最差适配选择
    fn select_worst_fit<'a>(&self, candidates: &[&'a NodeInfo], requirements: &ResourceRequirements) -> Result<&'a NodeInfo> {
        let node = candidates
            .iter()
            .max_by(|a, b| {
                let waste_a = self.calculate_resource_waste(a, requirements);
                let waste_b = self.calculate_resource_waste(b, requirements);
                waste_a.partial_cmp(&waste_b).unwrap_or(Ordering::Equal)
            })
            .copied()
            .ok_or_else(|| anyhow!("没有可用节点"))?;

        Ok(node)
    }

    /// 首次适配选择
    fn select_first_fit<'a>(&self, candidates: &[&'a NodeInfo]) -> Result<&'a NodeInfo> {
        candidates
            .first()
            .copied()
            .ok_or_else(|| anyhow!("没有可用节点"))
    }

    /// 轮询选择
    fn select_round_robin<'a>(&self, candidates: &[&'a NodeInfo]) -> Result<&'a NodeInfo> {
        let index = self.round_robin_counter
            .fetch_add(1, std::sync::atomic::Ordering::Relaxed) % candidates.len();
        Ok(candidates[index])
    }

    /// 最少负载选择
    fn select_least_loaded<'a>(&self, candidates: &[&'a NodeInfo]) -> Result<&'a NodeInfo> {
        let node = candidates
            .iter()
            .min_by(|a, b| {
                a.load_percentage.partial_cmp(&b.load_percentage).unwrap_or(Ordering::Equal)
            })
            .copied()
            .ok_or_else(|| anyhow!("没有可用节点"))?;

        Ok(node)
    }

    /// 资源感知选择
    fn select_resource_aware<'a>(
        &self,
        candidates: &[&'a NodeInfo],
        requirements: &ResourceRequirements,
        selector: Option<&NodeSelector>,
    ) -> Result<&'a NodeInfo> {
        let mut scores: Vec<_> = candidates
            .iter()
            .map(|node| {
                let resource_fit = self.calculate_fit_score(node, requirements);
                let load_score = 1.0 - node.load_percentage / 100.0;
                let affinity_score = selector
                    .map(|s| s.calculate_affinity_score(node))
                    .unwrap_or(0.0);

                // 综合评分：资源适配40%，负载30%，亲和性30%
                let total_score = resource_fit * 0.4 + load_score * 0.3 + affinity_score * 0.3;

                PlacementScore {
                    node_id: node.node_id.clone(),
                    score: total_score,
                    details: PlacementScoreDetails {
                        resource_fit,
                        load_score,
                        affinity_score,
                        location_score: 0.0,
                    },
                }
            })
            .collect();

        scores.sort_by(|a, b| b.score.partial_cmp(&a.score).unwrap_or(Ordering::Equal));

        let selected_node_id = &scores[0].node_id;
        candidates
            .iter()
            .find(|node| node.node_id == *selected_node_id)
            .copied()
            .ok_or_else(|| anyhow!("未找到选中的节点"))
    }

    /// 计算适配度评分
    fn calculate_fit_score(&self, node: &NodeInfo, requirements: &ResourceRequirements) -> f64 {
        let memory_ratio = requirements.memory_mb as f64 / node.available_resources.memory_mb as f64;
        let cpu_ratio = requirements.cpu_cores as f64 / node.available_resources.cpu_cores as f64;
        let disk_ratio = requirements.disk_mb as f64 / node.available_resources.disk_mb as f64;
        let network_ratio = requirements.network_bandwidth_mbps as f64 / node.available_resources.network_bandwidth_mbps as f64;

        let max_ratio = memory_ratio.max(cpu_ratio).max(disk_ratio).max(network_ratio);
        
        // 返回1.0减去最大资源比例，值越接近1.0表示适配度越好
        (1.0 - max_ratio).max(0.0)
    }

    /// 计算资源浪费
    fn calculate_resource_waste(&self, node: &NodeInfo, requirements: &ResourceRequirements) -> f64 {
        let memory_waste = node.available_resources.memory_mb.saturating_sub(requirements.memory_mb);
        let cpu_waste = node.available_resources.cpu_cores.saturating_sub(requirements.cpu_cores);
        let disk_waste = node.available_resources.disk_mb.saturating_sub(requirements.disk_mb);
        
        // 计算总浪费（简化计算）
        memory_waste as f64 + cpu_waste as f64 * 1000.0 + disk_waste as f64 * 0.1
    }

    /// 更改放置策略
    pub fn set_strategy(&mut self, strategy: PlacementStrategy) {
        self.strategy = strategy;
    }

    /// 获取当前策略
    pub fn get_strategy(&self) -> PlacementStrategy {
        self.strategy
    }
}

impl NodeSelector {
    /// 创建新的节点选择器
    pub fn new() -> Self {
        Self {
            weights: HashMap::new(),
            affinity_rules: Vec::new(),
            anti_affinity_rules: Vec::new(),
        }
    }

    /// 设置节点权重
    pub fn set_node_weight(&mut self, node_id: String, weight: f64) {
        self.weights.insert(node_id, weight);
    }

    /// 添加亲和性规则
    pub fn add_affinity_rule(&mut self, rule: NodeAffinityRule) {
        self.affinity_rules.push(rule);
    }

    /// 添加反亲和性规则
    pub fn add_anti_affinity_rule(&mut self, rule: NodeAffinityRule) {
        self.anti_affinity_rules.push(rule);
    }

    /// 计算亲和性评分
    fn calculate_affinity_score(&self, node: &NodeInfo) -> f64 {
        let mut total_score = 0.0;
        let mut total_weight = 0.0;

        // 计算亲和性规则评分
        for rule in &self.affinity_rules {
            let matches = rule.label_selector.iter().all(|(key, value)| {
                node.labels.get(key).map(|v| v == value).unwrap_or(false)
            });

            if matches {
                let weight = rule.weight.unwrap_or(1.0);
                total_score += weight;
                total_weight += weight;
            } else if rule.required {
                // 硬约束不满足，直接返回0分
                return 0.0;
            }
        }

        // 计算反亲和性规则评分
        for rule in &self.anti_affinity_rules {
            let matches = rule.label_selector.iter().all(|(key, value)| {
                node.labels.get(key).map(|v| v == value).unwrap_or(false)
            });

            if matches {
                if rule.required {
                    // 硬反亲和性约束匹配，直接返回0分
                    return 0.0;
                }
                let weight = rule.weight.unwrap_or(1.0);
                total_score -= weight;
                total_weight += weight;
            }
        }

        // 应用节点权重
        if let Some(node_weight) = self.weights.get(&node.node_id) {
            total_score *= node_weight;
        }

        if total_weight == 0.0 {
            0.5 // 默认中性评分
        } else {
            (total_score / total_weight).clamp(0.0, 1.0)
        }
    }
}

impl Default for NodeSelector {
    fn default() -> Self {
        Self::new()
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    fn create_test_node(id: &str, memory_mb: u64, cpu_cores: u32, load: f64) -> NodeInfo {
        NodeInfo {
            node_id: id.to_string(),
            address: format!("192.168.1.{}", id.chars().last().unwrap()),
            available_resources: ResourceRequirements {
                memory_mb,
                cpu_cores,
                disk_mb: 10240,
                network_bandwidth_mbps: 1000,
            },
            used_resources: ResourceRequirements {
                memory_mb: 0,
                cpu_cores: 0,
                disk_mb: 0,
                network_bandwidth_mbps: 0,
            },
            load_percentage: load,
            labels: HashMap::from([
                ("zone".to_string(), "us-west-1a".to_string()),
                ("instance-type".to_string(), "m5.large".to_string()),
            ]),
        }
    }

    fn create_test_requirements() -> ResourceRequirements {
        ResourceRequirements {
            memory_mb: 1024,
            cpu_cores: 2,
            disk_mb: 2048,
            network_bandwidth_mbps: 100,
        }
    }

    #[tokio::test]
    async fn test_best_fit_placement() {
        let engine = PlacementEngine::new(PlacementStrategy::BestFit);
        let nodes = vec![
            create_test_node("node1", 2048, 4, 20.0),  // 良好匹配
            create_test_node("node2", 8192, 8, 10.0),  // 资源过多
            create_test_node("node3", 1024, 2, 30.0),  // 刚好满足
        ];
        let requirements = create_test_requirements();

        let selected = engine.select_node(&nodes, &requirements, None).await.unwrap();
        assert!(selected.is_some());
        // BestFit应该选择资源最匹配的节点
    }

    #[tokio::test]
    async fn test_worst_fit_placement() {
        let engine = PlacementEngine::new(PlacementStrategy::WorstFit);
        let nodes = vec![
            create_test_node("node1", 2048, 4, 20.0),
            create_test_node("node2", 8192, 8, 10.0),  // 应该被选中（资源最多）
            create_test_node("node3", 1024, 2, 30.0),
        ];
        let requirements = create_test_requirements();

        let selected = engine.select_node(&nodes, &requirements, None).await.unwrap();
        assert_eq!(selected, Some("node2".to_string()));
    }

    #[tokio::test]
    async fn test_least_loaded_placement() {
        let engine = PlacementEngine::new(PlacementStrategy::LeastLoaded);
        let nodes = vec![
            create_test_node("node1", 2048, 4, 50.0),
            create_test_node("node2", 2048, 4, 10.0),  // 应该被选中（负载最低）
            create_test_node("node3", 2048, 4, 30.0),
        ];
        let requirements = create_test_requirements();

        let selected = engine.select_node(&nodes, &requirements, None).await.unwrap();
        assert_eq!(selected, Some("node2".to_string()));
    }

    #[tokio::test]
    async fn test_round_robin_placement() {
        let engine = PlacementEngine::new(PlacementStrategy::RoundRobin);
        let nodes = vec![
            create_test_node("node1", 2048, 4, 20.0),
            create_test_node("node2", 2048, 4, 20.0),
        ];
        let requirements = create_test_requirements();

        let selected1 = engine.select_node(&nodes, &requirements, None).await.unwrap();
        let selected2 = engine.select_node(&nodes, &requirements, None).await.unwrap();
        
        assert!(selected1.is_some());
        assert!(selected2.is_some());
        assert_ne!(selected1, selected2); // 应该轮询选择不同节点
    }

    #[tokio::test]
    async fn test_insufficient_resources() {
        let engine = PlacementEngine::new(PlacementStrategy::BestFit);
        let nodes = vec![
            create_test_node("node1", 512, 1, 20.0),  // 资源不足
        ];
        let requirements = create_test_requirements();

        let selected = engine.select_node(&nodes, &requirements, None).await.unwrap();
        assert!(selected.is_none());
    }

    #[tokio::test]
    async fn test_node_selector_affinity() {
        let mut selector = NodeSelector::new();
        selector.add_affinity_rule(NodeAffinityRule {
            name: "zone_affinity".to_string(),
            label_selector: HashMap::from([
                ("zone".to_string(), "us-west-1a".to_string()),
            ]),
            weight: Some(1.0),
            required: false,
        });

        let node = create_test_node("node1", 2048, 4, 20.0);
        let score = selector.calculate_affinity_score(&node);
        assert!(score > 0.0); // 应该有正的亲和性评分
    }

    #[tokio::test]
    async fn test_node_selector_anti_affinity() {
        let mut selector = NodeSelector::new();
        selector.add_anti_affinity_rule(NodeAffinityRule {
            name: "instance_anti_affinity".to_string(),
            label_selector: HashMap::from([
                ("instance-type".to_string(), "m5.large".to_string()),
            ]),
            weight: Some(1.0),
            required: false,
        });

        let node = create_test_node("node1", 2048, 4, 20.0);
        let score = selector.calculate_affinity_score(&node);
        assert!(score < 0.5); // 应该有负面影响
    }

    #[tokio::test]
    async fn test_resource_aware_placement_with_selector() {
        let engine = PlacementEngine::new(PlacementStrategy::ResourceAware);
        let nodes = vec![
            create_test_node("node1", 2048, 4, 20.0),
            create_test_node("node2", 2048, 4, 10.0),
        ];
        let requirements = create_test_requirements();

        let mut selector = NodeSelector::new();
        selector.set_node_weight("node2".to_string(), 1.5);

        let selected = engine.select_node(&nodes, &requirements, Some(&selector)).await.unwrap();
        assert!(selected.is_some());
    }
}